const CFG = require('../config');
const FUN = require('../comm/func');
const rCache = require('../model/redis_cache');
const DB = require('../model/model_data');
const dbHelp = require('../comm/db-helper');
const timLib = require('../comm/time-lib');
const dataCalc = require('../comm/data-calc');
const mbLib = require('../comm/modbus-lib');
const PMR = require('../utils/policyManger');

module.exports = class MeterZT
{
  //将数据库选项缓存到redis
  //policy 来自数据库的记录
  //opts 数据库json字段的解析对象
  //CacheCallback 存储执行函数
  static InitCache(policy,opts,CacheCallback)
  {
    let obj=PMR.baseParam(policy);//即将缓存到redis的参数

    //检查json选项
    if( parseInt(policy.record_limit)==0 )
    {
      console.error('wrong meter-zt params',policy.id);
      return false;
    }

    //json options参数存储
    obj.fetch_period = opts.fetch_period;//采样周期
    obj.cache_expire = Number(opts.fetch_period)+60;//数据缓存reids有效期
    obj.record_limit = policy.record_limit;//数据库条数上限
    obj.record_duration = 0;//数据库周期上限

    obj.data_width='4:f';
    obj.wlist = opts.wlist;//寄存器白名单
    if(opts.out) obj.out = opts.out;//输出变量列表
    if(opts.calc) obj.calc = opts.calc;//输出表达式

    //数据库处理
    dbHelp.createTab(obj.table,policy.uid);
    obj.out_tab=obj.table;//防止dataCalc函数不工作

    //缓存主站参数
    obj.hwid = policy.master;
    obj.role = 'master';
    obj.brother = policy.brother;
    CacheCallback(policy.id,policy.master,obj);//记录到缓存

    //缓存从站参数
    if(Number(policy.brother) && policy.brother!=-1)
    {
      obj.hwid = policy.brother;
      obj.role = 'slave';
      obj.brother = policy.master;
      CacheCallback(policy.id,obj.hwid,obj);//记录到缓存
    }
  }//end InitCache


  //modbus数据总分析
  // topic = {project,devType,func,devId}
  // dConf即InitCache中的obj
  // dataBuf即mq消息内容，buf对象
  static async DataIn(topic, dConf, dataBuf)
  {
    mbLib.mbPacketParse(topic.func, dConf, dataBuf, (last,sConf)=>{
      if(parseInt(sConf.fetch_period)==0) //实时存储
      {
        rCache.getKey(PMR.rdNode(dConf),'db_stamp').then((db_stamp)=>{
          let stamp=timLib.NowStamp();
          if(db_stamp!=null && stamp-db_stamp<CFG.DATA_SAVE_GAP)//存储到数据库的频率不能过快
          {pcyLog('[%d] real fast(%dmS)',dConf.hwid,stamp-db_stamp);return;}
          this.realHandler(last,sConf,dataBuf,stamp);
        })//end getKey
      }
      else //周期性存储
      {
        this.dataHandler(last,sConf,dataBuf,timLib.NowStamp());
      }
    })
  }//end function ModbusTx


  //普通的数据处理,每隔一段时间就求平均值入数据库
  static dataHandler(last,dConf,data,stamp)
  {
    let nodeDevStamp=PMR.rdNode(dConf)+':'+timLib.Rounding(dConf.fetch_period,stamp);
    let nodeDevAddr=nodeDevStamp+':'+last.addr;

    //处理前一个数据，为提高效率，此处不能用await
    rCache.get(nodeDevStamp,'addrs',last.addr).then((val)=>{
      if(val!=last.addr) //不存在,说明现在是第一个数据,那么可以处理旧数据
      {
        dataCalc.prevDataCalc(PMR.rdNode(dConf),dConf,'ave',(mode,fields,dArray)=>{
          if(dConf.table.includes('dat_json') && mode=='var-json') {
            //pcyLog('save',dConf.out_tab,fields,dArray)
            DB.newRcd(dConf.uid,dConf.table,fields,dArray);
            PMR.policyInform('update',dConf);
          }
        })
      }
    })

    //计数
    rCache.incKey(nodeDevAddr,'count',dConf.cache_expire);

    //记地址
    let obj={};
    obj[last.addr]=last.addr;
    rCache.set(nodeDevStamp,'addrs',obj,dConf.cache_expire);

    //记数值
    let wlist=dConf.wlist.split(',');//存储白名单
    let data_fmt=dConf.data_width.split(':');//分割数据宽度和数据格式
    for(let i=0;i<last.regNum;i+=(data_fmt[0]/2))
    {
      let reg=Number(last.regStart)+i;
      if(wlist.includes('#'+last.addr) || wlist.includes('#'+last.addr+':'+reg))
      {
        let regVal=(Number(data[3+i*2])<<8)+Number(data[4+i*2]);

        if(data_fmt[0]==4)//不同字节长度得的处理
          regVal=(regVal<<16)+(Number(data[5+i*2])<<8)+Number(data[6+i*2]);

        if(data_fmt[1]=='f') regVal=FUN.ieee745_toFloat(regVal).toFixed(4);

        rCache.push(nodeDevAddr,'reg.'+reg,regVal,dConf.cache_expire);//存储到缓存
      }
    }
    //pcyLog('cache',nodeDevAddr);
  }//end funciton


  //实时数据处理，直接入库
  static realHandler(last,dConf,data,stamp)
  {
    let save=[];
    let wlist=dConf.wlist.split(',');//存储白名单
    let data_fmt=dConf.data_width.split(':');//分割数据宽度和数据格式
    for(let i=0;i<last.regNum;i+=(data_fmt[0]/2))
    {
      let reg=Number(last.regStart)+i;
      if(wlist.includes('#'+last.addr) || wlist.includes('#'+last.addr+':'+reg))
      {
        let regVal=(Number(data[3+i*2])<<8)+Number(data[4+i*2]);

        if(data_fmt[0]==4)//不同字节长度得的处理
          regVal=(regVal<<16)+(Number(data[5+i*2])<<8)+Number(data[6+i*2]);

          if(data_fmt[1]=='f') regVal=FUN.ieee745_toFloat(regVal).toFixed(4);

        save.push({reg:reg,value:regVal,name:'#'+last.addr+':'+reg});
      }
    }

    //输出到数据库
    dataCalc.saveOutReg(dConf,save,last.addr,stamp,(mode,fields,dArray)=>{
      if(dConf.table.includes('dat_json') && mode=='var-json'){DB.newRcd(dConf.uid,dConf.table,fields,dArray);PMR.policyInform('update',dConf);}
    })
    rCache.setKey(PMR.rdNode(dConf),'db_stamp',stamp,60);//记录数据库访问时间戳
    pcyLog('[%d] Real addr%d',dConf.policy_id,last.addr,stamp,timLib.Stamp2Str(stamp));

    //删除数据库中冗余的前期数据
    dataCalc.dbRecordTidy(dConf.uid,dConf.table,dConf.policy_id,dConf.record_limit);
    if(dConf.out && dConf.calc && dConf.out_tab)
      dataCalc.dbRecordTidy(dConf.uid,dConf.out_tab,dConf.policy_id,dConf.record_limit);
  }









} //end class Modbus